# -*- coding: UTF-8 -*-
"""
HaaS506 RTU Temperature Sensor Library
Function: RS485 Modbus temperature and humidity sensor communication
Author: HaaS506 Development Team
Version: v1.0.0

Sensor Specifications:
- Address: 0x0B (11)
- Function Code: 0x04 (Read Input Registers)
- Register 0x0000-0x0001: Humidity and Temperature data
- Data Format: (aa*256+bb)/10 for humidity, (cc*256+dd)/10 for temperature
- Baud Rate: 4800
- Data Bits: 8
- Stop Bits: 1
- Parity: None
"""

from driver import UART
import utime as time

# Global UART object
rs485_uart = None


def init():
    """Initialize RS485 UART for temperature sensor communication"""
    global rs485_uart

    try:
        rs485_uart = UART()
        rs485_uart.open("RS485")
        print("Temperature sensor UART initialized")
        return True
    except Exception as e:
        print("Failed to initialize temperature sensor UART:", e)
        return False


def cleanup():
    """Clean up resources"""
    global rs485_uart

    try:
        if rs485_uart:
            rs485_uart.close()
            print("Temperature sensor UART closed")
    except Exception as e:
        print("Error during cleanup:", e)


def calculate_crc16_modbus(data):
    """Calculate CRC16 for Modbus RTU (for reference, current implementation uses pre-calculated CRC)"""
    # This is a reference implementation - current code uses pre-calculated CRC values
    crc = 0xFFFF
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x0001:
                crc = (crc >> 1) ^ 0xA001
            else:
                crc >>= 1
    return crc


def read_temperature_humidity():
    """
    Read temperature and humidity sensor data via RS485 Modbus

    Returns:
        tuple: (humidity, temperature) - both as float values
               Returns (None, None) if communication fails
    """
    if not rs485_uart:
        print("Error: Temperature sensor not initialized")
        return None, None

    try:
        # Modbus RTU Command Format:
        # Address: 0x0B (11)
        # Function: 0x04 (Read Input Registers)
        # Start Address: 0x0000 (0)
        # Quantity: 0x0002 (2 registers)
        # CRC: 0x7161 (calculated for this specific command)
        cmd = bytearray([0x0B, 0x04, 0x00, 0x00, 0x00, 0x02, 0x71, 0x61])

        print("Modbus Command Details:")
        print("  Raw command:", [hex(b) for b in cmd])
        print("  Command string:", "".join("{:02X}".format(b) for b in cmd))
        print("  Address: 0x{:02X} ({})".format(cmd[0], cmd[0]))
        print("  Function: 0x{:02X} (Read Input Registers)".format(cmd[1]))
        print("  Start Addr: 0x{:04X}".format((cmd[2] << 8) | cmd[3]))
        print("  Register Count: {}".format((cmd[4] << 8) | cmd[5]))
        print("  CRC: 0x{:04X}".format((cmd[7] << 8) | cmd[6]))

        # Clear UART buffer before sending command
        if rs485_uart.any() > 0:
            old_data = bytearray(50)
            bytes_cleared = rs485_uart.read(old_data)
            print("  Cleared {} old bytes from buffer".format(bytes_cleared))

        # Send command
        rs485_uart.write(cmd)
        print("  Command sent successfully")

        # Read response
        response = bytearray(9)
        bytes_received = rs485_uart.read(response)
        print("  Response received: {} bytes".format(bytes_received))

        print("Modbus Response Details:")
        print("  Bytes received: {}".format(bytes_received))

        if bytes_received > 0:
            print("  Raw response:", [hex(b) for b in response[:bytes_received]])

            if bytes_received >= 1:
                print("  Response address: 0x{:02X}".format(response[0]))
            if bytes_received >= 2:
                print("  Response function: 0x{:02X}".format(response[1]))
            if bytes_received >= 3:
                print("  Data length: {}".format(response[2]))
        else:
            print("  No response received")
            return None, None

        # Validate response format
        if bytes_received >= 9:
            # Verify address
            if response[0] != 0x0B:
                print(
                    "  Error: Address mismatch (expected 0x0B, got 0x{:02X})".format(
                        response[0]
                    )
                )
                return None, None

            # Verify function code
            if response[1] != 0x04:
                print(
                    "  Error: Function code mismatch (expected 0x04, got 0x{:02X})".format(
                        response[1]
                    )
                )
                return None, None

            # Verify data length
            if response[2] != 0x04:
                print(
                    "  Error: Data length mismatch (expected 4, got {})".format(
                        response[2]
                    )
                )
                return None, None

            # Parse data bytes
            aa = response[3]  # Humidity high byte
            bb = response[4]  # Humidity low byte
            cc = response[5]  # Temperature high byte
            dd = response[6]  # Temperature low byte

            # Calculate values according to sensor specification
            # Humidity = (aa*256+bb)/10
            # Temperature = (cc*256+dd)/10
            humidity = (aa * 256 + bb) / 10.0
            temperature = (cc * 256 + dd) / 10.0

            print("  Data parsing (sensor format):")
            print("    aa={}, bb={}, cc={}, dd={}".format(aa, bb, cc, dd))
            print("    Humidity: ({}*256+{})/10 = {:.1f} %RH".format(aa, bb, humidity))
            print(
                "    Temperature: ({}*256+{})/10 = {:.1f} °C".format(
                    cc, dd, temperature
                )
            )

            # Verify CRC if available
            if bytes_received >= 9:
                received_crc = (response[8] << 8) | response[7]
                print("  CRC: 0x{:04X}".format(received_crc))

            return humidity, temperature

        else:
            print(
                "  Error: Insufficient response data (need 9 bytes, got {})".format(
                    bytes_received
                )
            )
            return None, None

    except Exception as e:
        print("  Exception in sensor reading:", e)
        return None, None


def test_sensor_communication():
    """Test sensor communication and display detailed information"""
    print("\n=== Temperature Sensor Communication Test ===")

    if not rs485_uart:
        print("Error: Sensor not initialized. Call init() first.")
        return False

    print("Testing sensor communication...")

    # Perform multiple readings for reliability test
    success_count = 0
    total_attempts = 3

    for attempt in range(total_attempts):
        print("\nAttempt {}/{}:".format(attempt + 1, total_attempts))

        humidity, temperature = read_temperature_humidity()

        if humidity is not None and temperature is not None:
            success_count += 1
            print(
                "  SUCCESS - Humidity: {:.1f} %RH, Temperature: {:.1f} °C".format(
                    humidity, temperature
                )
            )

            # Basic validation
            if 0 <= humidity <= 100 and -40 <= temperature <= 80:
                print("  Data validation: PASS")
            else:
                print("  Data validation: WARNING - values outside expected range")
        else:
            print("  FAILED - No valid data received")

        # Wait between attempts
        if attempt < total_attempts - 1:
            time.sleep(1)

    print("\nTest Results:")
    print(
        "  Success rate: {}/{} ({:.1f}%)".format(
            success_count, total_attempts, (success_count / total_attempts) * 100
        )
    )

    if success_count >= total_attempts // 2:
        print("  Overall result: PASS")
        return True
    else:
        print("  Overall result: FAIL")
        return False


def get_sensor_info():
    """Get sensor information and specifications"""
    info = {
        "name": "RS485 Modbus Temperature & Humidity Sensor",
        "address": "0x0B (11)",
        "function_code": "0x04 (Read Input Registers)",
        "registers": "0x0000-0x0001 (Humidity & Temperature)",
        "data_format": "(high_byte*256+low_byte)/10",
        "baud_rate": "4800",
        "data_bits": "8",
        "stop_bits": "1",
        "parity": "None",
        "humidity_range": "0-100 %RH",
        "temperature_range": "-40 to +80 °C",
        "accuracy": "±2% RH, ±0.3°C",
    }
    return info


def print_sensor_info():
    """Print detailed sensor information"""
    print("\n=== Temperature Sensor Information ===")
    info = get_sensor_info()

    for key, value in info.items():
        print("  {}: {}".format(key.replace("_", " ").title(), value))
    print("=" * 40)


# Module test function
if __name__ == "__main__":
    print("Temperature Sensor Library Test")
    print("=" * 40)

    # Initialize sensor
    if init():
        # Print sensor information
        print_sensor_info()

        # Test communication
        test_sensor_communication()

        # Cleanup
        cleanup()
    else:
        print("Failed to initialize sensor")
